#ifndef _GLSUNIFORMBLOCKCASE_HPP
#define _GLSUNIFORMBLOCKCASE_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL (ES) Module
 * -----------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Uniform block tests.
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"
#include "tcuTestCase.hpp"
#include "gluShaderUtil.hpp"

namespace glu
{
class RenderContext;
}

namespace deqp
{
namespace gls
{

// Uniform block details.
namespace ub
{

enum UniformFlags
{
	PRECISION_LOW		= (1<<0),
	PRECISION_MEDIUM	= (1<<1),
	PRECISION_HIGH		= (1<<2),
	PRECISION_MASK		= PRECISION_LOW|PRECISION_MEDIUM|PRECISION_HIGH,

	LAYOUT_SHARED		= (1<<3),
	LAYOUT_PACKED		= (1<<4),
	LAYOUT_STD140		= (1<<5),
	LAYOUT_ROW_MAJOR	= (1<<6),
	LAYOUT_COLUMN_MAJOR	= (1<<7),	//!< \note Lack of both flags means column-major matrix.
	LAYOUT_MASK			= LAYOUT_SHARED|LAYOUT_PACKED|LAYOUT_STD140|LAYOUT_ROW_MAJOR|LAYOUT_COLUMN_MAJOR,

	DECLARE_VERTEX		= (1<<8),
	DECLARE_FRAGMENT	= (1<<9),
	DECLARE_BOTH		= DECLARE_VERTEX|DECLARE_FRAGMENT,

	UNUSED_VERTEX		= (1<<10),	//!< Uniform or struct member is not read in vertex shader.
	UNUSED_FRAGMENT		= (1<<11),	//!< Uniform or struct member is not read in fragment shader.
	UNUSED_BOTH			= UNUSED_VERTEX|UNUSED_FRAGMENT
};

// \todo [2012-07-25 pyry] Use glu::VarType.

class StructType;

class VarType
{
public:
						VarType			(void);
						VarType			(const VarType& other);
						VarType			(glu::DataType basicType, deUint32 flags);
						VarType			(const VarType& elementType, int arraySize);
	explicit			VarType			(const StructType* structPtr);
						~VarType		(void);

	bool				isBasicType		(void) const	{ return m_type == TYPE_BASIC;	}
	bool				isArrayType		(void) const	{ return m_type == TYPE_ARRAY;	}
	bool				isStructType	(void) const	{ return m_type == TYPE_STRUCT;	}

	deUint32			getFlags		(void) const	{ return m_flags;					}
	glu::DataType		getBasicType	(void) const	{ return m_data.basicType;			}

	const VarType&		getElementType	(void) const	{ return *m_data.array.elementType;	}
	int					getArraySize	(void) const	{ return m_data.array.size;			}

	const StructType&	getStruct		(void) const	{ return *m_data.structPtr;			}

	VarType&			operator=		(const VarType& other);

private:
	enum Type
	{
		TYPE_BASIC,
		TYPE_ARRAY,
		TYPE_STRUCT,

		TYPE_LAST
	};

	Type				m_type;
	deUint32			m_flags;
	union Data
	{
		glu::DataType		basicType;
		struct
		{
			VarType*		elementType;
			int				size;
		} array;
		const StructType*	structPtr;

		Data (void)
		{
			array.elementType	= DE_NULL;
			array.size			= 0;
		};
	} m_data;
};

class StructMember
{
public:
						StructMember	(const char* name, const VarType& type, deUint32 flags) : m_name(name), m_type(type), m_flags(flags) {}
						StructMember	(void) : m_flags(0) {}

	const char*			getName			(void) const { return m_name.c_str();	}
	const VarType&		getType			(void) const { return m_type;			}
	deUint32			getFlags		(void) const { return m_flags;			}

private:
	std::string			m_name;
	VarType				m_type;
	deUint32			m_flags;
};

class StructType
{
public:
	typedef std::vector<StructMember>::iterator			Iterator;
	typedef std::vector<StructMember>::const_iterator	ConstIterator;

								StructType		(const char* typeName) : m_typeName(typeName) {}
								~StructType		(void) {}

	const char*					getTypeName		(void) const	{ return m_typeName.empty() ? DE_NULL : m_typeName.c_str();	}

	inline Iterator				begin			(void)			{ return m_members.begin();		}
	inline ConstIterator		begin			(void) const	{ return m_members.begin();		}
	inline Iterator				end				(void)			{ return m_members.end();		}
	inline ConstIterator		end				(void) const	{ return m_members.end();		}

	void						addMember		(const char* name, const VarType& type, deUint32 flags = 0);

private:
	std::string					m_typeName;
	std::vector<StructMember>	m_members;
};

class Uniform
{
public:
					Uniform			(const char* name, const VarType& type, deUint32 flags = 0);

	const char*		getName			(void) const { return m_name.c_str();	}
	const VarType&	getType			(void) const { return m_type;			}
	deUint32		getFlags		(void) const { return m_flags;			}

private:
	std::string		m_name;
	VarType			m_type;
	deUint32		m_flags;
};

class UniformBlock
{
public:
	typedef std::vector<Uniform>::iterator			Iterator;
	typedef std::vector<Uniform>::const_iterator	ConstIterator;

							UniformBlock		(const char* blockName);

	const char*				getBlockName		(void) const { return m_blockName.c_str();		}
	const char*				getInstanceName		(void) const { return m_instanceName.empty() ? DE_NULL : m_instanceName.c_str();	}
	bool					isArray				(void) const { return m_arraySize > 0;			}
	int						getArraySize		(void) const { return m_arraySize;				}
	deUint32				getFlags			(void) const { return m_flags;					}

	void					setInstanceName		(const char* name)			{ m_instanceName = name;			}
	void					setFlags			(deUint32 flags)			{ m_flags = flags;					}
	void					setArraySize		(int arraySize)				{ m_arraySize = arraySize;			}
	void					addUniform			(const Uniform& uniform)	{ m_uniforms.push_back(uniform);	}

	inline Iterator			begin				(void)			{ return m_uniforms.begin();	}
	inline ConstIterator	begin				(void) const	{ return m_uniforms.begin();	}
	inline Iterator			end					(void)			{ return m_uniforms.end();		}
	inline ConstIterator	end					(void) const	{ return m_uniforms.end();		}

private:
	std::string				m_blockName;
	std::string				m_instanceName;
	std::vector<Uniform>	m_uniforms;
	int						m_arraySize;	//!< Array size or 0 if not interface block array.
	deUint32				m_flags;
};

class ShaderInterface
{
public:
								ShaderInterface			(void);
								~ShaderInterface		(void);

	StructType&					allocStruct				(const char* name);
	const StructType*			findStruct				(const char* name) const;
	void						getNamedStructs			(std::vector<const StructType*>& structs) const;

	UniformBlock&				allocBlock				(const char* name);

	int							getNumUniformBlocks		(void) const	{ return (int)m_uniformBlocks.size();	}
	const UniformBlock&			getUniformBlock			(int ndx) const	{ return *m_uniformBlocks[ndx];			}

private:
	std::vector<StructType*>	m_structs;
	std::vector<UniformBlock*>	m_uniformBlocks;
};

class UniformLayout;

} // ub

class UniformBlockCase : public tcu::TestCase
{
public:
	enum BufferMode
	{
		BUFFERMODE_SINGLE = 0,	//!< Single buffer shared between uniform blocks.
		BUFFERMODE_PER_BLOCK,	//!< Per-block buffers

		BUFFERMODE_LAST
	};

								UniformBlockCase			(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, glu::GLSLVersion glslVersion, BufferMode bufferMode);
								~UniformBlockCase			(void);

	IterateResult				iterate						(void);

protected:
	bool						compareStd140Blocks			(const ub::UniformLayout& refLayout, const ub::UniformLayout& cmpLayout) const;
	bool						compareSharedBlocks			(const ub::UniformLayout& refLayout, const ub::UniformLayout& cmpLayout) const;
	bool						compareTypes				(const ub::UniformLayout& refLayout, const ub::UniformLayout& cmpLayout) const;
	bool						checkLayoutIndices			(const ub::UniformLayout& layout) const;
	bool						checkLayoutBounds			(const ub::UniformLayout& layout) const;
	bool						checkIndexQueries			(deUint32 program, const ub::UniformLayout& layout) const;

	bool						render						(deUint32 program) const;

	glu::RenderContext&			m_renderCtx;
	glu::GLSLVersion			m_glslVersion;
	BufferMode					m_bufferMode;
	ub::ShaderInterface			m_interface;
};

} // gls
} // deqp

#endif // _GLSUNIFORMBLOCKCASE_HPP
